   #  #                                                #  #   
   #  #            ########################            #  #   
   #  #            #      AddMap v1.0     #            #  #   
   #  #            # Created by JoeyT2008 #            #  #   
   #  #            ########################            #  #   
   #  #                                                #  #   
##############################################################
# Description: AddMap allows you to easily download maps     #
# from the net onto your server, and also your FTP if fast-  #
# download is enabled. It checks given web index's for the   #
# map you want to download, and if it finds it, it will      #
# retrieve the file, store it, unzip/unbzip/bzip and shove   #
# it in your maps folder. All within about 10 seconds after  #
# initial buffer. If AddMap does not find the map, theres    #
# always the option to check FPSbanana for it! And if found  #
# there, the same procedure is repeated.                     #
##############################################################
                #  #                     #  #                 
              #################################               
              # Thanks for reading, JoeyT2008 #               
              #################################               

# Imports
import es, sys, time, urllib, cfglib, threading, pickle, os, thread, usermsg, playerlib, popuplib, gamethread, urlparse, httplib, ftplib

# Variables
load = True
addmap_updatingfpsb = False
addmap_svnurl = "http://addmap.googlecode.com/svn/trunk/"
addmap_revision = None
addmap_binpath = es.getAddonPath("addmap") + "/bin/"
addmap_temppath = es.getAddonPath("addmap") + "/temp/"
addmap_mappath = es.ServerVar("eventscripts_gamedir") + "/maps/"
if os.name == "nt":
    import bz2, zipfile
    addmap_wget = es.getAddonPath("addmap") + "/bin/wget.exe"
    addmap_rar = addmap_binpath + "unrar.exe"
else:
    addmap_wget = "wget"
    addmap_rar = addmap_binpath + "unrar"
    addmap_bzip = addmap_binpath + "bzip"
    addmap_bunzip = addmap_binpath + "bunzip"
    addmap_zip = addmap_binpath + "zip"

# Arrays
addmap_allowedexts = ["bsp", "bz2", "zip", "rar"]
addmap_commands = {"addmap": "addmap/AddMapCMD", "addmap_cancel": "addmap/cancelDownload", "addmap_clinput": "addmap/getInput"}
gameSupport = {"Counter-Strike: Source": 2, "ZombieMod": 72, "GunGame": 490, "Day of Defeat: Source": 10, "Team Fortress": 297}
mapTypes = {"Counter-Strike: Source": {"CS - Hostage Rescue": 25, "DE - Bomb/Defuse": 26, "AIM - Combat/Skill": 29, "PA - Prepared Assault": 30, "KA - Knife Arena": 31, "HE - Grenade War": 32, "FY - Fight Yard": 33, "DM - Deathmatch": 40, "AWP - Sniper War": 41, "BA - Battle Arena": 45, "FUN - Fun Type": 49, "SCOUT - Scout Sniper War": 53, "SURF - Surf Style": 56, "GLASS - Glass Fun": 57, "CL - Climb": 58, "RATS - Rats Style": 59, "HC - Happy Camper": 120, "AS - Assassination": 257, "PB - Paintball": 511, "SG - Shotgun": 2095, "BH - Bunny Hop": 2976, "IS - Ice Skating": 3718, "HST - Hosties": 3719, "DR - Death Run":  3796, "TZ - Trikz": 3884, "Other": 34}, "Team Fortress": {"CP - Control Point": 1836, "CTF - Capture the Flag": 1837, "? - Other": 1268, "DM - Deathmatch": 2218}, "Day of Defeat: Source": {"DOD - Standard": 94, "? - Other": 590, "DM - Deathmatch": 98, "FY - Fightyard": 105}, "GunGame": {"GG - GunGame": 894, "GGAIM - GunGame Combat/Skill": 1774, "GGFY - GunGame Fight Yard": 1775}, "ZombieMod": {"ZM - Zombie Mod": 975, "ZE - Zombie Escape": 2471}}
maplist = {}
fpsbmaplist = {}
downloads = {}
maplistcheck = []

# Plugin Information
info			=	es.AddonInfo() 
info.name		=	"AddMap: Automatic map installer."
info.version	=	1.0
info.url		=	"http://forums.mattie.info/cs/forums/viewtopic.php?t=21872"
info.basename	=	"addmap"
info.author		=	"JoeyT2008"

es.ServerVar("addmap_ver", info.version, "The version of AddMap which is running on this server.").makepublic()

# Generate Config
config = cfglib.AddonCFG(es.getAddonPath("addmap") + "/config.cfg")
config.text("---------------------------")
config.text("AddMap v%s - Configuration")
config.text("---------------------------")
addmap_urls = config.cvar("addmap_urls", "http://www.tkdami.net/~dgames/download/css/maps/;", "Web index's to scan for maps. Seperate each url with a semi-colon.")
addmap_admins = config.cvar("addmap_admins", "STEAM_ID_LAN;", "Players that are allowed to AddMap. Seperate each steamid with a semi-colon.")
addmap_fastdownload = config.cvar("addmap_fastdownload", 0, "If fast download is enabled on the server, set this to 1.")
addmap_fpsbanana = config.cvar("addmap_fpsbanana", 1, "If the map could not be found in the web index's, search fpsbanana for it?")
addmap_mapinfo = config.cvar("addmap_mapinfo", 1, "Show map information for fpsbanana maps? Disable if you are concerned about speed.")
addmap_allfpsb = config.cvar("addmap_browsefpsb", 1, "Generate a list of every FPSbanana map then show them in the !maps menu?")
addmap_timeout = config.cvar("addmap_timeout", 600, "Timeout after X seconds of downloading one map. If you plan on downloading large maps, set this high.")
addmap_publicprogress = config.cvar("addmap_publicprogress", 1, "Show map downloading progress to all players on the server? 1 for all players, 0 for admins only")
addmap_filter = config.cvar("addmap_filter", "", "Map prefix filter, this will make AddMap only show maps that start with this prefix. Seperate each filter with a semi-colon.")
addmap_autoadd = config.cvar("addmap_autoadd", 1, "Add maps downloaded via AddMap to the servers maplist? This also includes mani votemap list.")
addmap_overwrite = config.cvar("addmap_overwrite", 1, "Overwrite existing maps with those downloaded from AddMap?")
addmap_dlmethod = config.cvar("addmap_dlmethod", 1, "Method of http access. 1: WGET; 2: URLLIB (python)")
addmap_autoupdate = config.cvar("addmap_autoupdate", 1, "Allow AddMap to automatically update itself?")
config.text("-----------------------------------")
config.text("FTP Configuration for Fast-Download")
config.text("-----------------------------------")
addmap_ftpupload = config.cvar("addmap_ftpupload", 0, "Upload maps to FTP for fast-download?")
addmap_ftphost = config.cvar("addmap_ftphost", "example.com", "FTP hostname, this can be an ip address or an url.")
addmap_ftpport = config.cvar("addmap_ftpport", 21, "FTP port, if you are unsure, stick with 21.")
addmap_ftplogin = config.cvar("addmap_ftplogin", "Username", "FTP username, would be best to use the root account.")
addmap_ftppassword = config.cvar("addmap_ftppassword", "********", "FTP password for the account specified above.")
addmap_ftppath = config.cvar("addmap_ftppath", "/www/cstrike/maps/", "Exact path to the maps folder on the FTP with trailing slash.")
addmap_ftpoverwrite = config.cvar("addmap_ftpoverwrite", 0, "Overwrite existing maps on the FTP server?")
config.write()

# Basic Functions
def load():
    global addmap_revision, load
    load = True
    if not es.getgame() in mapTypes:
        es.dbgmsg(0, "[AddMap] This game is unsupported by AddMap.")
        gamethread.delayed(0.1, es.unload, ("addmap"))
    config.execute()
    if not es.exists("command", "addmap"): es.regcmd("addmap", "addmap/AddMapCMD", "Automatic map search and download function, syntax: addmap <mapname> [add to maplist 1:0] [overwrite 1:0]")
    if not es.exists("command", "addmap_update"): es.regcmd("addmap_update", "addmap/AddMapUpdate", "Check for a newer version of addmap.")
    for command in addmap_commands: es.regclientcmd(command, addmap_commands[command])
    for command in ["!addmap", "!maps"]: es.regsaycmd(command, "addmap/popupInit")
    createMissing()
    addmap_revision = getCurRevision()
    loadMaplist()
    loadFPSBMaplist()
    updateMaplist()
    if addmap_autoupdate == 1: AutoUpdate().start()

def unload():
    global load
    load = False
    for command in addmap_commands: es.unregclientcmd(command)
    for command in ["!addmap", "!maps"]: es.unregsaycmd(command)
    removeTemp()

def getFilename(url):
    try:
        filename = url.split("/")
        filename = filename[len(filename)-1].split("\\")
        filename = filename[len(filename)-1]
    except: filename = None
    return filename

def getEXT(url):
    try: return url.split(".")[len(url.split("."))-1]
    except: return None

def moveFile(ffrom, fto):
    try:
        if os.path.exists(fto): os.remove(fto)
        os.rename(ffrom, fto)
    except: pass

def conPrint(text, userid=None):
    try:
        if userid: es.tell(userid, "#multi", text.replace("[AddMap]", "#green[AddMap]#lightgreen"))
        else: es.dbgmsg(0, text)
    except: pass

def format(string, format=","):
    end = None
    try:
        split = str(string).split(".")
        if len(split) == 2: string, end = split[0], split[1]
        check = int(string)
    except: return string
    string, formatted, rev, num, total, output = str(string), [], [], 0, 0, ""
    for number in string: rev.append(number)
    rev.reverse()
    for number in rev:
        formatted.append(number)
        num += 1
        total += 1
        if num == 3:
            if not total == len(rev): formatted.append(format)
            num = 0
    formatted.reverse()
    for number in formatted: output += number
    if not end == None: output += ".%s" % end
    return output

def validUrl(url):
    if url.startswith("http://") or url.startswith("ftp://"): return True
    else: return False

def mapExists(map):
    return os.path.exists("%s\maps\%s.bsp" % (str(es.ServerVar("eventscripts_gamedir")), map))

def getMapUrl(map):
    if map in maplist:
        for url in maplist[map]:
            if validUrl(url): return url
    if map in fpsbmaplist:
        for url in fpsbmaplist[map]:
            if validUrl(url): return url
    return None

def createMissing():
    for folder in ["/bin/", "/temp/", "/temp/logs/", "/temp/zip/", "/temp/rar/"]:
        if not os.path.exists(es.getAddonPath("addmap") + folder): os.mkdir(es.getAddonPath("addmap") + folder)
    for file in ["/maps.txt", "/revision.txt"]:
        if not os.path.exists(es.getAddonPath("addmap") + file): open(es.getAddonPath("addmap") + file, "w").close()
    for am in ["/maplist.am", "/fpsb.am", "/downloaded.am"]:
        if not os.path.exists(es.getAddonPath("addmap") + am):
            file = open(es.getAddonPath("addmap") + am, "w")
            pickle.dump({}, file)
            file.close()

def removeTemp():
    for root, dirs, files in os.walk(addmap_temppath):
        for file in files:
            try: os.remove(root + "/" + file)
            except: pass
    for tmp in os.listdir(str(es.ServerVar("eventscripts_gamedir")) + "/../"):
        if tmp.startswith("wget-log"):
            try: os.remove(str(es.ServerVar("eventscripts_gamedir")) + "/../" + tmp)
            except: pass

def getQueuedNum():
    queuedx = 0
    for download in downloads:
        try:
            if downloads[download][0].isQueued() and not downloads[download][0].started: queuedx += 1
        except: pass
    return queuedx

def getQueued():
    queued = []
    for download in downloads:
        try:
            if downloads[download][0].isQueued() and not downloads[download][0].started: queued.append(download)
        except: pass
    return queued

def addMaplists(map):
    # Generate maplist.
    maps = []
    maplist = open(es.ServerVar("eventscripts_gamedir") + "/maplist.txt", "r").read()
    for x in maplist.split("\n"):
        if not x.strip() == "": maps.append(x.strip() + "\n")
    if not map + "\n" in maps: maps.append(map + "\n")
    # Add to 'maplist.txt'.
    maplist = open(es.ServerVar("eventscripts_gamedir") + "/maplist.txt", "w")
    maplist.writelines(maps)
    maplist.close
    # Add to 'mapcycle.txt'.
    mapcycle = open(es.ServerVar("eventscripts_gamedir") + "/mapcycle.txt", "w")
    mapcycle.writelines(maps)
    mapcycle.close
    # If exists, add to 'votemaplist.txt'
    if os.path.exists(es.ServerVar("eventscripts_gamedir") + "/cfg/mani_admin_plugin/votemaplist.txt"):
        votemaplist = open(es.ServerVar("eventscripts_gamedir") + "/cfg/mani_admin_plugin/votemaplist.txt", "w")
        votemaplist.writelines(maps)
        votemaplist.close

def addDownloaded(map, username):
    maps = loadDownloaded()
    if not map in maps:
        maps[map] = {}
        maps[map][0] = time.ctime()
        maps[map][1] = username
    saveDownloaded(maps)

def removeDownloaded(map):
    maps = loadDownloaded()
    if map in maps: del maps[map]
    saveDownloaded(maps)

def loadDownloaded():
    file = open(es.getAddonPath("addmap") + "/downloaded.am", "r")
    return pickle.load(file)

def saveDownloaded(maps):
    file = open(es.getAddonPath("addmap") + "/downloaded.am", "w")
    pickle.dump(maps, file)
    file.close()

def getCurRevision():
    file = open(es.getAddonPath("addmap") + "/revision.txt", "r")
    try: return int(file.read())
    except: return 0

# Downloading Maps
class threadDL:
    def __init__(self, map, url, path):
        self.map, self.url, self.path = map, url, path
        if addmap_dlmethod == 1: self.logname = addmap_temppath + "logs/%d.txt" % time.time()
        self.starttime = None
        self.endtime = None
        self.started = False
        self.finished = False
        self.failed = False
        self.count = None
        self.blockSize = None
        self.totalSize = None
        self.queued = self.isQueued()

    def update(self, *args):
        try: self.count, self.blockSize, self.totalSize = args[0], args[1], args[2]
        except: pass

    def download(self):
        if addmap_dlmethod == 1 and os.path.exists(self.logname): os.remove(self.logname)
        self.queued = self.isQueued()
        if not self.queued:
            if self.map in downloads:
                thread.start_new_thread(self.startDownload, ())
                self.started = True
                self.starttime = time.time()
        else: gamethread.delayedname(1, "download_%s" % self.map, self.download, ())

    def startDownload(self):
        if addmap_dlmethod == 1:
            if os.name == "nt": os.system(r'""%s" "%s" -b -O "%s" -o "%s"' % (addmap_wget.replace("/", "\\"), self.url.replace("\\", "/"), self.path.replace("/", "\\"), self.logname.replace("/", "\\")))
            else: os.system(r'wget "%s" -b -O "%s" -o "%s"' % (self.url.replace("\\", "/"), self.path.replace("/", "\\"), self.logname.replace("/", "\\")))
        else: urllib.urlretrieve(self.url, self.path, reporthook=self.update)
        self.finished = True
        self.endtime = time.time()

    def isQueued(self):
        queued = False
        for download in downloads:
            try:
                if downloads[download][0].started == True: queued = True
            except: pass
        return queued

    def readLog(self):
        if addmap_dlmethod == 1:
            if self.checkFile(): return open(self.logname, "r").read()
            else: return ""
        else: return ""

    def getDuration(self): return time.time() - self.starttime

    def getKBPS(self): return float("%.2f" % (float(self.getDownloaded()) / self.getDuration()))

    def getAverageKBPS(self):
        try: return float("%.2f" % (float(self.getSize() / (self.endtime - self.starttime))))
        except: return 0

    def getETA(self):
        try: return time.strftime("%H:%M:%S", time.gmtime(((self.getSize() - self.getDownloaded()) / self.getKBPS())))
        except: return "00:00:00"

    def getTaken(self):
        try: return time.strftime("%H:%M:%S", time.gmtime(self.endtime - self.starttime))
        except: return "00:00:00"

    def getSize(self):
        if addmap_dlmethod == 1:
            file = self.readLog()
            try: size = int(float(file.split("Length: ")[1].split(" [")[0].replace(",", "")) / 1024)
            except: size = 0
            return int(size)
        else:
            try: return int(self.totalSize / 1024)
            except: return 0

    def getDownloaded(self):
        size = self.getSize()
        downloaded = self.getCount()
        if downloaded > size: downloaded = size
        return int(downloaded)

    def getPercentage(self):
        try: return float("%.2f" % (float(self.getDownloaded()) / float(self.getSize()) * 100))
        except: return 0

    def getCount(self):
        if addmap_dlmethod == 1:
            file = self.readLog()
            count = 0
            for line in file.split("\n"):
                try:
                    for dot in line.split("->")[1].split(" [")[0]:
                        if dot == ".": count += 1
                except: pass
            return count
        else:
            try: return int((self.count * self.blockSize) / 1024)
            except: return 0

    def checkFile(self):
        return os.path.exists(self.logname)

def downloadMap(url, path, add, userid=None):
    global downloads, maplistcheck
    mapname = getFilename(url)
    if mapname.split(".")[0] in maplistcheck: maplistcheck.remove(mapname.split(".")[0])
    if add == 1: maplistcheck.append(mapname.split(".")[0])
    if not mapname.split(".")[0] in downloads:
        downloads[mapname.split(".")[0]] = {}
        downloads[mapname.split(".")[0]][0] = threadDL(mapname.split(".")[0], url, path)
        downloads[mapname.split(".")[0]][1] = userid
    if downloads[mapname.split(".")[0]][0].isQueued(): conPrint("[AddMap] %s has been added to the download queue." % mapname.split(".")[0], userid)
    reportHook(downloads[mapname.split(".")[0]][0], mapname.split(".")[0], time.time())
    downloads[mapname.split(".")[0]][0].download()

def reportHook(download, map, xtime):
    global downloads
    if map in downloads:
        userid = downloads[map][1]
        if not "Giving up." in download.readLog():
            if not download.finished:
                if download.started:
                    if download.getPercentage() < 100 and download.getSize() > 0:
                        bar = "|"*(int(download.getPercentage())/4)
                        bar += " "*(25-(int(download.getPercentage())/4))
                        bar = "[" + bar + "]"
                        queued = getQueuedNum()
                        text = "Map: " + map + "\n " + str(download.getPercentage()) + "% - " + str(download.getDownloaded()) + "KB/" + str(download.getSize()) + "KB\n" + bar + "\nETA: " + download.getETA() + " @ " + str(download.getKBPS()) + "KB/s\n" + str(queued) + " Downloads In Queue"
                        if addmap_publicprogress == 1:
                            for userid in playerlib.getUseridList("#all"): usermsg.hudhint(userid, text)
                        else:
                            for steamid in addmap_admins:
                                userid = es.getuserid(steamid)
                                if es.exists("userid", userid): usermsg.hudhint(userid, text)
                elif time.time() > (xtime + 180):
                    download.finished = True
                    download.failed = True
                gamethread.delayed(0.1, reportHook, (download, map, xtime))
            elif not download.failed:
                bar = "[|||||||||||||||||||||||||]"
                queued = getQueuedNum()
                text = "Map: " + map + "\n 100% - " + str(download.getSize()) + "KB/" + str(download.getSize()) + "KB\n" + bar + "\nTook: " + download.getTaken() + " @ " + str(download.getAverageKBPS()) + "KB/s\n" + str(queued) + " Downloads In Queue"
                if addmap_publicprogress == 1:
                    for userid in playerlib.getUseridList("#all"): usermsg.hudhint(userid, text)
                else:
                    for steamid in addmap_admins:
                        userid = es.getuserid(steamid)
                        if es.exists("userid", userid): usermsg.hudhint(userid, text)
                doCompression(map, download.path)
            else:
                del downloads[map]
                conPrint("[AddMap] Failed to download '%s' as the webserver is currently unavailable, please try again later." % map, userid)
        else:
            del downloads[map]
            conPrint("[AddMap] Failed to download '%s' as the webserver is currently unavailable, please try again later." % map, userid)

def doCompression(map, path):
    ext = getEXT(path)
    userid = downloads[map][1]
    if os.path.exists(path):
        if ext == "bz2":
            conPrint("[AddMap] Decompressing BZ2 archive... Please wait...", userid)
            thread.start_new_thread(Compression(map, path).decompressBZ2, ())
        elif ext == "zip":
            conPrint("[AddMap] Decompressing ZIP archive... Please wait...", userid)
            thread.start_new_thread(Compression(map, path).decompressZIP, ())
        elif ext == "rar":
            conPrint("[AddMap] Decompressing RAR archive... Please wait...", userid)
            thread.start_new_thread(Compression(map, path).decompressRAR, ())

def afterCompression(map, path):
    global maplistcheck, downloads
    userid = downloads[map][1]
    if path and os.path.exists(path):
        moveFile(path, addmap_mappath + getFilename(path))
        conPrint("[AddMap] %s (%s) was successfully downloaded and moved to the correct directory." % (map, getFilename(path)), userid)
        if addmap_ftpupload == 1:
            conPrint("[AddMap] Uploading %s (%s) to ftp://%s:%s, this may take a while." % (map, getFilename(path), str(addmap_ftphost), str(addmap_ftpport)), userid)
            thread.start_new_thread(Compression(map, addmap_mappath + getFilename(path), userid).compressBZ2, ())
        if map in maplistcheck:
            maplistcheck.remove(map)
            addMaplists(map)
        if es.exists("userid", userid): username = es.getplayername(userid)
        else: username = "Console"
        addDownloaded(map, username)
        del downloads[map]
    else:
        del downloads[map]
        conPrint("[AddMap] An unknown error occured whilst decompressing the map. Please try again later.", userid)

# AddMap console function
def AddMapCMD():
    add, overwrite = 0, 0
    userid = es.getcmduserid()
    if not es.exists("userid", userid): userid = None
    if es.getargc() >= 2 and es.getargc() <= 4:
        try:
            if es.getargc() == 3 or es.getargc() == 4: add = int(es.getargv(2))
        except: add = 0
        try:
            if es.getargc() == 4: overwrite = int(es.getargv(3))
        except: overwrite = 0
        if es.getargv(1).startswith("http://"):
            url = es.getargv(1).replace("\\", "/")
            map = getFilename(url).split(".")[0]
            AddMap(map, add, overwrite, url, userid)
        elif es.getargv(1) == "http": conPrint("Please wrap the url in quotes.", userid)
        else:
            if addmap_fpsbanana == 1:
                try: FPSbanana(str(es.getargv(1)), add, overwrite, userid, int(es.getargv(1))).start()
                except: AddMap(es.getargv(1), add, overwrite, None, userid)
            else: AddMap(es.getargv(1), add, overwrite, None, userid)
    else: conPrint("Syntax: AddMap <map/id/url> [Add to Maplist 0/1] [Overwrite 0/1]", userid)

# Main AddMap Class
class AddMap:
    def __init__(self, map, add, overwrite, url=None, userid=None):
        self.map, self.add, self.overwrite, self.url, self.userid = map.lower(), add, overwrite, url, userid
        if not mapExists(self.map) or self.overwrite == 1:
            if self.url: url = self.url
            else: url = self.getMapUrl()
            if url: self.downloadMap(url)
            else: pass
        else: conPrint("[AddMap] %s already exists in the servers 'maps' directory." % self.map)

    def getMapUrl(self):
        conPrint("[AddMap] Searching for %s... Please wait..." % self.map, self.userid)
        url = getMapUrl(self.map)
        if not url:
            conPrint("[AddMap] %s was not found in the maplist." % self.map, self.userid)
            if addmap_fpsbanana == 1: FPSbanana(self.map, self.add, self.overwrite, self.userid).start()
        return url

    def downloadMap(self, url):
        if not self.url: conPrint("[AddMap] An URL for %s has been found in the maplist, starting download..." % self.map, self.userid)
        else: conPrint("[AddMap] Downloading %s from url..." % self.map, self.userid)
        downloadMap(url, addmap_temppath + url.split("/")[len(url.split("/"))-1], self.add, self.userid)

# Hacky method of retrieving download links from FPSbanana.
# If FPSbanana changes, the method will need to be updated.
class FPSbanana(threading.Thread):
    def __init__(self, map, add, overwrite, userid=None, id=None):
        self.map, self.add, self.overwrite, self.userid, self.id = map, add, overwrite, userid, id
        self.xnl = False
        threading.Thread.__init__(self)

    def run(self):
        gamethread.queue(conPrint, ("[AddMap] Checking FPSbanana for %s..." % self.map, self.userid))
        if self.id: self.xnl = True
        else: self.id = self.getID()
        if self.id:
            if not self.xnl: gamethread.queue(conPrint, ("[AddMap] %s (ID: %d) was found on FPSbanana!" % (self.map, int(self.id)), self.userid))
            url = self.getDownload()
            if url:
                if self.xnl and not self.map == str(self.id): gamethread.queue(conPrint, ("[AddMap] Found a download link for ID: %s, preparing to download..." % self.map, self.userid))
                else: gamethread.queue(conPrint, ("[AddMap] Found a download link for %s (ID: %d)!" % (self.map, self.id), self.userid))
                downloadMap(url, addmap_temppath + url.split("/")[len(url.split("/"))-1], self.add, self.userid)
            else:
                if self.xnl and not self.map == str(self.id): gamethread.queue(conPrint, ("[AddMap] Couldn't find a download link for ID: %s." % (self.map, self.id), self.userid))
                else: gamethread.queue(conPrint, ("[AddMap] Couldn't find a download link for %s (ID: %d)." % self.map, self.userid))
        else: gamethread.queue(conPrint, ("[AddMap] Unfortunately, %s was not found on FPSbanana." % self.map, self.userid))

    def getID(self):
        try:
            id = None
            game = es.getgame()
            if game in gameSupport:
                if gameSupport[game] == 2:
                    if self.map.startswith("gg_"): game = "GunGame"
                    elif self.map.startswith("zm_") or self.map.startswith("ze_"): game = "ZombieMod"
                search = self.getHTML("http://www.fpsbanana.com/maps/search?s%5Bmaps.name%5D=" + self.map + "&s%5Bmaps.tags%5D=&s%5Bmaptypes.catid%5D=" + str(gameSupport[game]) + "&s%5Busers.name%5D=&s%5Bmaps.author%5D=&s%5Bfiles.size%5D=0&s%5Bmaps.rating%5D=0&s%5Bmaps.players%5D=&s%5Bmaps.id%5D=&stype=all")
                for line in search.split("\n"):
                    if self.map in line:
                        try:
                            id = int(line.split("http://www.fpsbanana.com/maps/")[1].split('"')[0])
                            break
                        except: pass
                return id
            else: gamethread.queue(conPrint, ("[AddMap] %s is unsupported!" % es.getgame(), self.userid))
        except: gamethread.queue(conPrint, ("[AddMap] Couldn't check FPSbanana for %s!" % self.map, self.userid))

    def getDownload(self):
        try:
            if not self.xnl: gamethread.queue(conPrint, ("[AddMap] Retrieving download page for %s (ID: %d)..." % (self.map, self.id), self.userid))
            else: gamethread.queue(conPrint, ("[AddMap] Retrieving download page for ID: %s..." % self.map, self.userid))
            download = self.getHTML("http://www.fpsbanana.com/maps/download/%d" % int(self.id))
            for line in download.split("\n"):
                if "<strong>DOWNLOAD</strong>" in line:
                    checkurl = line.split('<a style="margin:0px 50px 0px 50px" class="green" href="')[1].split('"><strong>DOWNLOAD</strong>')[0]
                    if "&ext=" in checkurl:
                        ext = checkurl.split("&ext=")[1]
                        if ext in addmap_allowedexts:
                            checkurl = checkurl.split("?path=")[1].split("&sectionid")[0] + "." + ext
                            if validUrl(checkurl):
                                return checkurl
                                break
            return None
        except: gamethread.queue(conPrint, ("[AddMap] Couldn't check FPSbanana for %s!" % self.map, self.userid))

    def getHTML(self, url):
        if addmap_dlmethod == 1:
            path = addmap_temppath + "/logs/" + str(time.time()) + ".html"
            if os.name == "nt": process = os.system(r'""%s" "%s" -b -O "%s"' % (addmap_wget.replace("/", "\\"), url.replace("\\", "/"), path.replace("/", "\\")))
            else: process = os.system(r'wget "%s" -b -O "%s"' % (url.replace("\\", "/"), path.replace("/", "\\")))
            if os.path.exists(path): return open(path, "r").read()
            else: return None
        else: return urllib.urlopen(url).read()

class getAllFPSBMaps(threading.Thread):
    def __init__(self, userid=None):
        self.userid = userid
        threading.Thread.__init__(self)

    def run(self):
        global fpsbmaplist, addmap_updatingfpsb
        if not addmap_updatingfpsb:
            addmap_updatingfpsb = True
            game = es.getgame()
            if game in mapTypes:
                conPrint("[AddMap] Updating FPSbanana maplist, this may take a while...", self.userid)
                starttime = time.time()
                for maptype in mapTypes[game]:
                    if addmap_updatingfpsb:
                        if not maptype in fpsbmaplist: fpsbmaplist[maptype] = {}
                        try: pages = self.getHTML("http://www.fpsbanana.com/maptypes/%d" % mapTypes[game][maptype]).split('class="textbox"> of ')[1].split("&nbsp;")[0]
                        except: pages = 1
                        for page in range(int(pages)):
                            try:
                                if addmap_updatingfpsb:
                                    page += 1
                                    es.dbgmsg(1, "[AddMap] %s: Page %d of %d" % (maptype, page, int(pages)))
                                    pagehtml = self.getHTML("http://www.fpsbanana.com/maptypes/%d?maps_page=%d" % (mapTypes[es.getgame()][maptype], page))
                                    mapblocks = pagehtml.split('<form name="f_1" action="/maptypes/')[1].split("</table>")[0]
                                    for mapblock in mapblocks.split("\n"):
                                        if "<strong>" in mapblock and "</strong>" in mapblock:
                                            try:
                                                line = mapblock.split('"><br><a href="http://www.fpsbanana.com/maps/')[1].split('</strong>')[0]
                                                id = line.split('"')[0]
                                                map = line.split("<strong>")[1]
                                                if not map in fpsbmaplist[maptype]: fpsbmaplist[maptype][map] = id
                                            except: pass
                            except: pass
                if addmap_updatingfpsb: conPrint("[AddMap] Created a list of every FPSbanana map. Generating took %d seconds!" % int(time.time() - starttime), self.userid)
            else: conPrint("[AddMap] %s is unsupported!" % game, self.userid)
            addmap_updatingfpsb = False
        else: conPrint("[AddMap] AddMap is already updating the FPSbanana maplist, please be patient.", self.userid)

    def getHTML(self, url):
        if addmap_dlmethod == 1:
            path = addmap_temppath + "/logs/" + str(time.time()) + ".html"
            if os.name == "nt": process = os.system(r'""%s" "%s" -b -O "%s"' % (addmap_wget.replace("/", "\\"), url.replace("\\", "/"), path.replace("/", "\\")))
            else: process = os.system(r'wget "%s" -b -O "%s"' % (url.replace("\\", "/"), path.replace("/", "\\")))
            if os.path.exists(path): return open(path, "r").read()
            else: return None
        else: return urllib.urlopen(url).read()

class getXMaps(threading.Thread):
    def __init__(self, userid, type, maptype=None):
        self.userid, self.type, self.maptype = userid, type, maptype
        threading.Thread.__init__(self)

    def run(self):
        args = {}
        game = es.getgame()
        if game in gameSupport:
            if gameSupport[game] == 2:
                if str(es.ServerVar("eventscripts_currentmap")).startswith("gg_"): game = "GunGame"
                elif str(es.ServerVar("eventscripts_currentmap")).startswith("zm_") or str(es.ServerVar("eventscripts_currentmap")).startswith("ze_"): game = "ZombieMod"
            gameid = str(gameSupport[game])
            gamethread.queue(conPrint, ("[AddMap] Retrieving %s maps..." % {1: "latest", 2: "top", 3: self.maptype}[self.type], self.userid))
            try:
                maps = {}
                if self.type == 1:
                    if self.maptype == "All": mapblocks = self.getHTML("http://www.fpsbanana.com/maps/newest/" + str(game))
                    else: mapblocks = self.getHTML("http://www.fpsbanana.com/maptypes/" + str(mapTypes[es.getgame()][self.maptype]) + "?order=maps.date")
                elif self.type == 2:
                    if self.maptype == "All": mapblocks = self.getHTML("http://www.fpsbanana.com/maps/top/" + str(game))
                    else: mapblocks = self.getHTML("http://www.fpsbanana.com/maptypes/" + str(mapTypes[es.getgame()][self.maptype]) + "?order=maps.rating")
                if self.maptype == "All":
                    mapblocks = mapblocks.split('<form name="f_2" action="/maps/')[1].split("</table>")[0]
                else: mapblocks = mapblocks.split('<form name="f_1" action="/maptypes/')[1].split("</table>")[0]
                for mapblock in mapblocks.split("\n"):
                    if "<strong>" in mapblock and "</strong>" in mapblock:
                        line = mapblock.split('"><br><a href="http://www.fpsbanana.com/maps/')[1].split('</strong>')[0]
                        id = line.split('"')[0]
                        map = line.split("<strong>")[1]
                        maps[map] = id
                self.displayMaps(maps)
            except:
                gamethread.queue(conPrint, ("[AddMap] An unknown error occurred while retrieving information.", self.userid))
        else:
            gamethread.queue(conPrint, ("[AddMap] Game: %s is unsupported." % es.getgame(), self.userid))

    def displayMaps(self, maps):
        fpsb_maps = popuplib.easymenu("fpsb_maps", "_popup_choice", self.downloadPopupMap)
        if self.type == 1:
            if self.maptype == "All": fpsb_maps.settitle("-AddMap-\nLatest Maps")
            else: fpsb_maps.settitle("-AddMap-\nLatest %s Maps" % self.maptype)
        elif self.type == 2:
            if self.maptype == "All": fpsb_maps.settitle("-AddMap-\nTop Maps")
            else: fpsb_maps.settitle("-AddMap-\nTop %s Maps" % self.maptype)
        for map in maps:
            if not mapExists(map.split(".")[0]): fpsb_maps.addoption("%s,%s" % (map, maps[map]), map.split(".")[0])
        fpsb_maps.send(self.userid)

    def downloadPopupMap(self, userid, choice, popup):
        map = choice.split(",")[0]
        id = choice.split(",")[1]
        if addmap_mapinfo == 1: viewMapInfo(userid, id, map).start()
        else: FPSbanana(map, int(addmap_autoadd), int(addmap_overwrite), userid, id).start()

    def getHTML(self, url):
        if addmap_dlmethod == 1:
            path = addmap_temppath + "/logs/" + str(time.time()) + ".html"
            if os.name == "nt": process = os.system(r'""%s" "%s" -b -O "%s"' % (addmap_wget.replace("/", "\\"), url.replace("\\", "/"), path.replace("/", "\\")))
            else: process = os.system(r'wget "%s" -b -O "%s"' % (url.replace("\\", "/"), path.replace("/", "\\")))
            if os.path.exists(path): return open(path, "r").read()
            else: return None
        else: return urllib.urlopen(url).read()

# Maplist Functions
def loadMaplist():
    global maplist
    file = open(es.getAddonPath("addmap") + "/maplist.am", "r")
    maplist = pickle.load(file)
    conPrint("[AddMap] Loaded current master maplist.")

def updateMaplist():
    global maplist
    conPrint("[AddMap] Updating master maplist...")
    maps = generateMaplistFromFile()
    for url in maps:
        map = maps[url]
        if not map in maplist: maplist[map] = [url]
        else:
            if not url in maplist[map][0]: maplist[map].append(url)
    try: saveMaplist()
    except: pass
    generateMaplistFromWeb().start()

def saveMaplist():
    file = open(es.getAddonPath("addmap") + "/maplist.am", "w")
    pickle.dump(maplist, file)
    file.close()

# FPSbanana Maplist Functions
def loadFPSBMaplist():
    global fpsbmaplist
    file = open(es.getAddonPath("addmap") + "/fpsb.am", "r")
    fpsbmaplist = pickle.load(file)
    conPrint("[AddMap] Loaded current FPSbanana maplist.")

def saveFPSBMaplist():
    maps = open(es.getAddonPath("addmap") + "/fpsb.am", "w")
    cPickle.dump(fpsbmaplist, maps)
    maps.close()

# Generate Maplists
class generateMaplistFromWeb(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        global maplist
        maps = {}
        for url in str(addmap_urls).split(";"):
            if not url.strip() == "" and validUrl(url):
                html = getHTML(url)
                for line in html.split("\n"):
                    try:
                        line = line.lower()
                        map = line.split('href="')[1].split('"')[0]
                        ext = map.split(".")
                        if len(ext) > 1: ext = getEXT(map)
                        if ext in addmap_allowedexts and not map.endswith((".txt.bz2", ".nav.bz2", ".res.bz2")):
                            mapurl = url + map
                            if not map.split(".")[0] in maplist: maplist[map.split(".")[0]] = [mapurl]
                            else:
                                if not mapurl in maplist[map.split(".")][0]: maplist[map.split(".")].append(mapurl)
                    except: pass
                try: saveMaplist()
                except: pass
        try: conPrint("[AddMap] Finished updating master maplist!")
        except: pass

    def getHTML(self, url):
        if addmap_dlmethod == 1:
            path = addmap_temppath + "/logs/" + str(time.time()) + ".html"
            if os.name == "nt": process = os.system(r'""%s" "%s" -b -O "%s"' % (addmap_wget.replace("/", "\\"), url.replace("\\", "/"), path.replace("/", "\\")))
            else: process = os.system(r'wget "%s" -b -O "%s"' % (url.replace("\\", "/"), path.replace("/", "\\")))
            if os.path.exists(path): return open(path, "r").read()
            else: return None
        else: return urllib.urlopen(url).read()

def generateMaplistFromFile():
    file = open(es.getAddonPath("addmap") + "/maps.txt", "r").read()
    maps = {}
    for line in file.split("\n"):
        if not line.startswith("//") and line.strip() == "":
            map = line.split(";")
            try:
                if not map[1] in maps: maps[map[1]] = map[0]
                else: maps[map[1]].append(map[0])
            except: pass
    return maps

# Compression
class Compression:
    def __init__(self, map, file, userid=None):
        if userid: self.map, self.filename, self.userid = map, file, userid
        else:  self.map, self.filename, self.userid = map, file, downloads[map][1]

    def compressBZ2(self):
        bspfile = self.filename
        output = bspfile + ".bz2"
        if os.path.exists(bspfile):
            if not os.path.exists(output):
                if os.name == "nt":
                    openfile = open(bspfile, "rb").read()
                    file = bz2.BZ2File(output, "w")
                    file.write(openfile)
                    file.close()
                    gamethread.queue(conPrint, ("[AddMap] Map has been compressed to BZ2, preparing to upload to FTP..."))
                    FTPUpload(self.userid, self.map, output).start()
                else:
                    os.system(r'"%s" "%s" -k' % (addmap_bzip, bspfile))
                    gamethread.queue(conPrint, ("[AddMap] Map has been compressed to BZ2, preparing to upload to FTP..."))
                    FTPUpload(self.userid, self.map, output).start()
            else: FTPUpload(self.userid, self.map, output).start()
        else:
            gamethread.queue(conPrint, ("[AddMap] An unknown error occured whilst compressing the map for fast download."))

    def decompressBZ2(self):
        bz2file = self.filename
        output = bz2file.replace(".bz2", "")
        if os.path.exists(bz2file):
            if os.name == "nt":
                openfile = bz2.BZ2File(bz2file, "r").read()
                file = open(output, "wb")
                file.write(openfile)
                file.close()
                afterCompression(self.map, output)
            else:
                os.system(r'"%s" "%s" -k' % (addmap_bunzip, bz2file))
                afterCompression(self.map, output)
        else:
            del downloads[self.map]
            gamethread.queue(conPrint, ("[AddMap] An unknown error occured whilst decompressing the map. Please try again later.", self.userid))

    def decompressZIP(self):
        file = self.filename
        output = None
        try:
            if os.path.exists(file):
                if os.name == "nt":
                    zipobj = zipfile.ZipFile(file)
                    for name in zipobj.namelist():
                        filename = name.split("/")
                        if len(filename) > 0: filename = filename[len(filename)-1]
                        else: filename = name
                        if filename.endswith(".bsp"):
                            output = addmap_temppath + filename
                            outfile = open(output, "wb")
                            outfile.write(zipobj.read(name))
                            outfile.close()
                            break
                    if output: afterCompression(self.map, output)
                    else: gamethread.queue(conPrint, ("[AddMap] An unknown error occured whilst decompressing the map. Please try again later.", self.userid))
                else:
                    dirtime = time.time()
                    if not os.path.exists("%s/zip/%d/" % (addmap_temppath, dirtime)): os.mkdir("%s/zip/%d/" % (addmap_temppath, dirtime))
                    os.system(r'"%s" -o "%s" -d "%s/zip/%d/"' % (addmap_unzip, file, addmap_temppath, dirtime))
                    bsp = None
                    for root, dir, files in os.walk(addmap_temppath + "/zip/" + dirtime + "/"):
                        filelist = [os.path.join(root, fi) for fi in files if fi.endswith(".bsp")]
                        for bsp in filelist: pass
                    if bsp:
                        filename = bsp.split("\\")
                        if not len(filename) > 1: fname = bsp.split("/")
                        if len(filename) > 1:
                            filename = filename[len(filename)-1]
                            output = addmap_temppath + fname
                            os.rename(bsp, output)
                            afterCompression(self.map, output)
            if not output: gamethread.queue(conPrint, ("[AddMap] An unknown error occured whilst decompressing the map. Please try again later.", self.userid))
        except:
            del downloads[self.map]
            gamethread.queue(conPrint, ("[AddMap] An unknown error occured whilst decompressing the map. Please try again later.", self.userid))

    def decompressRAR(self):
        file = self.filename
        output = None
        try:
            if os.path.exists(file):
                dirtime = time.time()
                if not os.path.exists("%srar/%d/" % (addmap_temppath, dirtime)): os.mkdir("%s/rar/%d/" % (addmap_temppath, dirtime))
                if os.name == "nt": os.system(r'""%s" e "%s" "%srar/%d/"' % (addmap_rar, file, addmap_temppath, dirtime))
                else: os.system(r'"%s" e "%s" "%srar/%d/"' % (addmap_rar, file, addmap_temppath, dirtime))
                bsp = None
                for root, dir, files in os.walk("%srar/%d/" % (addmap_temppath, dirtime)):
                    filelist = [os.path.join(root, fi) for fi in files if fi.endswith(".bsp")]
                    for bsp in filelist: pass
                if bsp:
                    filename = bsp.replace("/", "\\").split("\\")
                    if len(filename) > 1:
                        filename = filename[len(filename)-1]
                        output = addmap_temppath + filename
                        os.rename(bsp, output)
                        afterCompression(self.map, output)
            if not output: gamethread.queue(conPrint, ("[AddMap] An unknown error occured whilst decompressing the map. Please try again later.", self.userid))
        except:
            del downloads[self.map]
            gamethread.queue(conPrint, ("[AddMap] An unknown error occured whilst decompressing the map. Please try again later.", self.userid))

def storbinary(self, cmd, fp, blocksize=8192, callback=None):
    self.voidcmd('TYPE I')
    conn = self.transfercmd(cmd)
    while 1:
        buf = fp.read(blocksize)
        if not buf: break
        conn.sendall(buf)
        if callback: callback(buf)
    conn.close()
    return self.voidresp()
ftplib.FTP.storbinary = storbinary

# FTP Upload
class FTPUpload(threading.Thread):
    def __init__(self, userid, map, filename):
        self.userid, self.map, self.filename = userid, map, filename
        self.downloaded = 0
        self.size = os.stat(self.filename).st_size
        threading.Thread.__init__(self)
        
    def run(self):
        try:
            if self.filename.split(".")[len(self.filename.split("."))-1] in addmap_allowedexts:
                ftp = ftplib.FTP()
                ftp.connect(str(addmap_ftphost), int(addmap_ftpport))
                gamethread.queue(conPrint, "[AddMap] Connected to: %s:%s." % (str(addmap_ftphost), str(addmap_ftpport)))
                ftp.login(str(addmap_ftplogin), str(addmap_ftppassword))
                gamethread.queue(conPrint, "[AddMap] Logged in as: %s." % str(addmap_ftplogin))
                ftp.cwd(str(addmap_ftppath))
                name = os.path.split(self.filename)[1]
                if not name in ftp.nlst() or addmap_ftpoverwrite == 1:
                    f = open(self.filename, "rb")
                    gamethread.queue(conPrint, "[AddMap] Uploading '%s' to FTP..." % self.map)
                    ftp.storbinary("STOR %s" % name, f, callback=self.update)
                    gamethread.queue(conPrint, ("[AddMap] '%s' was uploaded to FTP successfully!" % self.map, self.userid))
                ftp.quit()
            else:
                gamethread.queue(conPrint, ("[AddMap] '%s' could not be uploaded to FTP." % self.map, self.userid))
        except: gamethread.queue(conPrint, ("[AddMap] An error occured during the FTP transfer.", self.userid))

    def update(self, buffer):
        self.downloaded += len(buffer)-1
        percentage = float("%.2f" % (float(self.downloaded) / float(self.size) * 100))
        bar = "|" * (int(percentage)/4)
        bar += " "*(25-(int(percentage)/4))
        bar = "[" + bar + "]"
        es.dbgmsg(0, "Uploading... " + bar + " - " + str(percentage) + "%")

# AddMap Popup Menu
def popupInit():
    userid = str(es.getcmduserid())
    if es.getplayersteamid(userid) in str(addmap_admins).split(";"): popupMain(userid)
    else: conPrint("[AddMap] You are not authorised to run this command.", userid)

def popupMain(userid):
    p_addmap = popuplib.create("addmap")
    p_addmap.addline("-AddMap-")
    p_addmap.addline("---------------")
    p_addmap.addline("->1. Find a Map")
    p_addmap.addline("->2. Browse Maplist")
    p_addmap.addline("->3. Downloaded Maps")
    p_addmap.addline("->4. Download Queue")
    if addmap_fpsbanana == 1:
        if not addmap_updatingfpsb: p_addmap.addline("->5. Update FPSB Maplist")
        else: p_addmap.addline("->5. Stop Updating FPSB Maplist")
        if es.getgame() in mapTypes and addmap_allfpsb == 1:
            p_addmap.addline("->6. Browse FPSB Maplist")
            p_addmap.addline("->7. Top 25 FPSB Maps")
            p_addmap.addline("->8. Latest FPSB Maps")
        else:
            p_addmap.addline("->6. Top 25 FPSB Maps")
            p_addmap.addline("->7. Latest FPSB Maps")
    p_addmap.addline("---------------")
    p_addmap.addline("0. Close")
    p_addmap.menuselect = popupSelection
    p_addmap.send(userid)

def popupSelection(userid, choice, popup):
    if choice == 1: es.escinputbox(10, userid, "AddMap", "Map Name:", "addmap_clinput")
    elif choice == 2: popupMaplist(userid)
    elif choice == 3: popupDownloaded(userid)
    elif choice == 4: showDownloadQueue(userid)
    if addmap_fpsbanana == 1:
        if choice == 5:
            if not addmap_updatingfpsb: getAllFPSBMaps(userid).start()
            else: stopFPSBUpdate(userid)
        elif choice == 6:
            if es.getgame() in mapTypes: showFPSBMaptypes(userid)
            else: getFPSBMapTypes(userid, 2)
        elif choice == 7:
            if es.getgame() in mapTypes: getFPSBMapTypes(userid, 2)
            else: getFPSBMapTypes(userid, 1)
        elif choice == 8:
            if es.getgame() in mapTypes: getFPSBMapTypes(userid, 1)

def stopFPSBUpdate(userid):
    global addmap_updatingfpsb
    addmap_updatingfpsb = False
    conPrint("[AddMap] FPSbanana maplist update has been canceled!", userid)

def showFPSBMaptypes(userid):
    allmaps = 0
    p_maps = popuplib.easymenu("fpsbmaptypes", "_popup_choice", showFPSBMaps)
    p_maps.settitle("-AddMap-\nSelect a Map Type")
    for maptype in fpsbmaplist: allmaps += len(fpsbmaplist[maptype])
    if str(addmap_filter).strip() == "" or len(str(addmap_filter).split(";")) == 0: p_maps.addoption("All Maps", "(" + str(allmaps) + ") " + "All Maps")
    for maptype in fpsbmaplist:
        if str(addmap_filter).strip() == "" or len(str(addmap_filter).split(";")) == 0 or maptype.split()[0].lower() in str(addmap_filter).split(";"):
            if len(fpsbmaplist[maptype]) > 0: p_maps.addoption(maptype, "(" + str(len(fpsbmaplist[maptype])) + ") " + maptype)
    p_maps.send(userid)

def showFPSBMaps(userid, choice, popup):
    p_FPSBmaps = popuplib.easymenu("fpsbmaps", "_popup_choice", selectFPSBMap)
    p_FPSBmaps.settitle("-AddMap-\n%s" % choice)
    if not choice == "All Maps":
        for map in fpsbmaplist[choice]:
            if not mapExists(map.split(".")[0]): p_FPSBmaps.addoption(map + "," + fpsbmaplist[choice][map], map)
    else:
        for maptype in fpsbmaplist:
            for map in fpsbmaplist[maptype]:
                if not mapExists(map.split(".")[0]): p_FPSBmaps.addoption(map + "," + fpsbmaplist[maptype][map], map)
    p_FPSBmaps.send(userid)

def selectFPSBMap(userid, choice, popup):
    map = choice.split(",")[0]
    id = choice.split(",")[1]
    if addmap_mapinfo == 1: viewMapInfo(userid, id, map).start()
    else: FPSbanana(map, int(addmap_autoadd), int(addmap_overwrite), userid, id).start()

class viewMapInfo(threading.Thread):
    def __init__(self, userid, id, map):
        self.userid, self.id, self.map = userid, id, map
        threading.Thread.__init__(self)

    def run(self):
        conPrint("[AddMap] Retrieving map information...", self.userid)
        try:
            mapinfo = self.getMapInfo()
            p_mapinfo = popuplib.create("mapinfo")
            p_mapinfo.addline("-AddMap-")
            p_mapinfo.addline("---------------")
            p_mapinfo.addline("%s - %s" % (self.map, mapinfo["rating"]))
            p_mapinfo.addline("---------------")
            try: p_mapinfo.addline("Author: %s" % mapinfo["author"])
            except: pass
            try: p_mapinfo.addline("Version: %s" % mapinfo["version"])
            except: pass
            try: p_mapinfo.addline("Filesize: %s" % mapinfo["filesize"])
            except: pass
            try: p_mapinfo.addline("Max Players: %s" % mapinfo["players"])
            except: pass
            try: p_mapinfo.addline("Map Size: %s" % mapinfo["mapsize"])
            except: pass
            try: p_mapinfo.addline("Weapons: %s" % mapinfo["weapons"])
            except: pass
            try: p_mapinfo.addline("Misc Features: %s" % mapinfo["miscfeatures"])
            except: pass
            p_mapinfo.addline("---------------")
            p_mapinfo.addline("->1. Download")
            p_mapinfo.addline("0. Close")
            p_mapinfo.menuselect = self.downloadMap
            p_mapinfo.send(self.userid)
        except: conPrint("[AddMap] Failed to retrieve map information!", self.userid)

    def getMapInfo(self):
        html = self.getHTML("http://www.fpsbanana.com/maps/%s" % str(self.id))
        map = {}
        try: map["rating"] = html.split('bScore</a><br><big><b class="orange">')[1].split("</b>")[0] + "/10"
        except: map["rating"] = "No Rating"
        try: map["author"] = html.split("<b>Author(s):</b> ")[1].split("</td>")[0].split("[maps.author]=")[1].split('"')[0]
        except: map["author"] = "Not Specified"
        try: map["release"] = html.split('<b>Release:</b> <b class="green">')[1].split("</b>")[0]
        except: map["release"] = "Not Specified"
        try: map["version"] = html.split('<div class="row"><b>Version:</b>')[1].split("</div>")[0]
        except: map["version"] = "Not Specified"
        try: map["filesize"] = html.split('<div class="row_alt"><b>Filesize:</b>')[1].split("</div>")[0]
        except: map["filesize"] = "Not Specified"
        try: map["players"] = html.split('<div class="row"><b>Players:</b>')[1].split("</div>")[0]
        except: map["players"] = "Not Specified"
        try: map["mapsize"] = html.split('<div class="row_alt"><b>Map Size:</b>')[1].split("</div>")[0]
        except: map["mapsize"] = "Not Specified"
        try: map["weapons"] = html.split('<div class="row"><b>Weapons:</b>')[1].split("</div>")[0]
        except: map["weapons"] = "Not Specified"
        try: map["miscfeatures"] = html.split('<div class="row_alt"><b>Misc. Features:</b>')[1].split("</div>")[0]
        except: map["miscfeatures"] = "Not Specified"
        return map

    def downloadMap(self, userid, choice, popup): FPSbanana(self.map, int(addmap_autoadd), int(addmap_overwrite), userid, self.id).start()

    def getHTML(self, url):
        if addmap_dlmethod == 1:
            path = addmap_temppath + "/logs/" + str(time.time()) + ".html"
            if os.name == "nt": process = os.system(r'""%s" "%s" -b -O "%s"' % (addmap_wget.replace("/", "\\"), url.replace("\\", "/"), path.replace("/", "\\")))
            else: process = os.system(r'wget "%s" -b -O "%s"' % (url.replace("\\", "/"), path.replace("/", "\\")))
            if os.path.exists(path): return open(path, "r").read()
            else: return None
        else: return urllib.urlopen(url).read()
        
def showDownloadQueue(userid):
    p_maps = popuplib.easymenu("queued", "_popup_choice", cancelQueued)
    p_maps.settitle("-AddMap-\nQueued Downloads")
    for map in getQueued():
        p_maps.addoption(map, map)
    p_maps.send(userid)

def cancelQueued(userid, choice, popup):
    global downloads
    if choice in downloads:
        del downloads[choice]
        conPrint("[AddMap] %s has been removed from the download queue." % choice, userid)

def getFPSBMapTypes(userid, type):
    p_FPSBmaptypes = popuplib.easymenu("FPSBmaptypes", "_popup_choice", selectFPSBMapType)
    p_FPSBmaptypes.settitle("-AddMap-\nSelect a MapType")
    if str(addmap_filter) == "" or len(str(addmap_filter).split(";")) == 0: p_FPSBmaptypes.addoption("All," + str(type), "All Maps")
    for maptype in mapTypes[es.getgame()]:
        if str(addmap_filter) == "" or len(str(addmap_filter).split(";")) == 0 or maptype.split()[0].lower() in str(addmap_filter).split(";"): p_FPSBmaptypes.addoption(maptype + "," + str(type), maptype)
    p_FPSBmaptypes.send(userid)

def selectFPSBMapType(userid, choice, popup):
    maptype = choice.split(",")[0]
    type = int(choice.split(",")[1])
    getXMaps(userid, type, maptype).start()

def popupMaplist(userid):
    p_maps = popuplib.easymenu("maps", "_popup_choice", downloadPopupMap)
    p_maps.settitle("-AddMap-\nAvailable Maps")
    for map in maplist:
        if not os.path.exists(addmap_mappath + map + ".bsp") and not map in loadDownloaded() and not map in downloads:
            if str(addmap_filter) == "" or len(str(addmap_filter).split(";")) == 0 or map.split("_")[0] in str(addmap_filter).split(";"): p_maps.addoption("%s|||%s" % (map, maplist[map][0]), map)
    p_maps.send(userid)

def popupDownloaded(userid):
    p_maps = popuplib.easymenu("downloaded", "_popup_choice", removePopupMapConfirm)
    p_maps.settitle("-AddMap-\nDownloaded Maps")
    for map in loadDownloaded():
        if os.path.exists(addmap_mappath + map + ".bsp"): p_maps.addoption("%s" % map, map)
    p_maps.send(userid)

def downloadPopupMap(userid, choice, popup):
    AddMap(choice.split("|||")[0], int(addmap_autoadd), int(addmap_overwrite), choice.split("|||")[1], userid)

def removePopupMapConfirm(userid, choice, popup):
    downloaded = loadDownloaded()
    p_mapinfo = popuplib.create("mapinfo|||%s" % choice)
    p_mapinfo.addline("-AddMap-")
    p_mapinfo.addline("---------------")
    p_mapinfo.addline("%s" % choice)
    p_mapinfo.addline("---------------")
    p_mapinfo.addline("Downloader: %s" % downloaded[choice][1])
    p_mapinfo.addline("Date: %s" % downloaded[choice][0])
    p_mapinfo.addline("---------------")
    p_mapinfo.addline("->1. Delete")
    p_mapinfo.addline("0. Close")
    p_mapinfo.menuselect = removePopupMap
    p_mapinfo.send(userid)

def removePopupMap(userid, choice, popup):
    map = popup.split("|||")[1]
    removeDownloaded(map)
    if os.path.exists(addmap_mappath + map + ".bsp"): os.remove(addmap_mappath + map + ".bsp")
    conPrint("[AddMap] '%s' has been removed from the server and AddMap logs." % map, userid)

def cancelPopup(*args): pass

def cancelDownload():
    global downloads
    userid = es.getcmduserid()
    map = es.getargs()
    if not map or map.strip() == "":
        downloads = {}
        conPrint("[AddMap] All downloads have been canceled.", userid)
    else:
        if map in downloads:
            del downloads[map]
            conPrint("[AddMap] %s has been canceled." % map, userid)
        else:
            conPrint("[AddMap] %s is not queued for download." % map, userid)

def getInput():
    if es.getargc() > 1:
        try:
            if addmap_fpsbanana == 1:
                id = int(es.getargs())
                FPSbanana(id, int(addmap_autoadd), int(addmap_overwrite), es.getcmduserid(), id).start()
            else: AddMap(es.getargs(), int(addmap_autoadd), int(addmap_overwrite), None, es.getcmduserid())
        except: AddMap(es.getargs(), int(addmap_autoadd), int(addmap_overwrite), None, es.getcmduserid())

# Simple SVN Class to fetch the files and revision from the Google Code SVN.
class AutoUpdate(threading.Thread):
    def __init__(self):
        self.url = addmap_svnurl
        threading.Thread.__init__(self)

    def run(self):
        global addmap_revision
        revision = self.getRevision()
        es.dbgmsg(0, "[AddMap] Checking for updates...")
        if int(addmap_revision) < int(revision):
            es.msg("#multi", "#green[AddMap] #defaultA new version of #lightgreenAddMap#default is available, updating from revision '#lightgreen%d#default' to revision '#lightgreen%d#default'." % (addmap_revision, revision))
            branches = self.getBranches()
            folders = self.getFolders(branches)
            for folder in folders:
                path = es.getAddonPath("addmap") + "/" + folder
                es.dbgmsg(1, "[AddMap] Creating folder: '%s'." % folder)
                if not os.path.exists(path): os.mkdir(path)
            files = self.getFiles(branches)
            for file in files:
                url = self.url + file
                path = es.getAddonPath("addmap") + "/" + file
                es.dbgmsg(1, "[AddMap] Updating file: '%s'." % file)
                self.downloadFile(url, path)
            addmap_revision = int(revision)
            self.setRevision()
            es.msg("#multi", "#green[AddMap] #lightgreenAddMap#default has been updated to revision '#lightgreen%d#default'." % revision)
            es.reload("addmap")
        else: es.dbgmsg(0, "[AddMap] Addmap is up to date.")

    def downloadFile(self, url, path):
        tmppath = addmap_temppath + "/logs/" + str(time.time()) + ".html"
        if addmap_dlmethod == 1:
            if os.name == "nt": process = os.system(r'""%s" "%s" -b -O "%s"' % (addmap_wget.replace("/", "\\"), url.replace("\\", "/"), tmppath.replace("/", "\\")))
            else: process = os.system(r'wget "%s" -b -O "%s"' % (url.replace("\\", "/"), tmppath.replace("/", "\\")))
            if os.path.exists(tmppath):
                tdl = True
                try:
                    if len(open(tmppath).read()) == len(open(path).read()): tdl = False
                except: pass
                if tdl:
                    if os.path.exists(path):
                        os.remove(path)
                    moveFile(tmppath, path)
        else:
            download = urllib.urlopen(url)
            tdl = True
            try:
                if download.info()["content-length"] == len(open(path).read()): tdl = False
            except: pass
            if tdl:
                file = open(tmppath, "wb")
                file.write(download.read())
                file.close()
                if os.path.exists(tmppath):
                    if os.path.exists(path): os.remove(path)
                    moveFile(tmppath, path)

    def getBranches(self, url=None):
        path = ""
        if url: path = url.replace(self.url, "", 1)
        else: url = self.url
        branches = []
        svn = self.getHTML(url)
        for line in svn.split("\n"):
            if "<li>" in line:
                branch = line.split('<li><a href="')[1].split('"')[0]
                if not branch == "../":
                    branches.append(path + branch)
                    if branch.endswith("/"): branches += self.getBranches(url + branch)
        return branches

    def getFolders(self, branches):
        folders = []
        for branch in branches:
            if branch.endswith("/"): folders.append(branch)
        return folders

    def getFiles(self, branches):
        files = []
        for branch in branches:
            if not branch.endswith("/"): files.append(branch)
        return files

    def getRevision(self):
        branches = []
        svn = self.getHTML(self.url)
        revision = 0
        for line in svn.split("\n"):
            if '<h2>Revision ' in line: revision = line.split('<h2>Revision ')[1].split(":")[0]
        try: return int(revision)
        except: return 0

    def setRevision(self):
        file = open(es.getAddonPath("addmap") + "/revision.txt", "w")
        try: file.write(str(addmap_revision))
        except: file.write("0")
        file.close()

    def getHTML(self, url):
        if addmap_dlmethod == 1:
            path = addmap_temppath + "/logs/" + str(time.time()) + ".html"
            if os.name == "nt": process = os.system(r'""%s" "%s" -b -O "%s"' % (addmap_wget.replace("/", "\\"), url.replace("\\", "/"), path.replace("/", "\\")))
            else: process = os.system(r'wget "%s" -b -O "%s"' % (url.replace("\\", "/"), path.replace("/", "\\")))
            if os.path.exists(path): return open(path, "r").read()
            else: return None
        else: return urllib.urlopen(url).read()

def AddMapUpdate(): AutoUpdate().start()